package tools.NW3D;

public abstract class DynamicProgramming3d {

	protected String sequence1;
	protected String sequence2;
	protected Cell3d[][][] scoreTable;
	protected boolean tableIsFilledIn;
	protected boolean isInitialized;
	
	
	
	public DynamicProgramming3d(String sequence1, String sequence2) {
		super();
		this.sequence1 = sequence1;
		this.sequence2 = sequence2;
		scoreTable = new Cell3d[sequence1.length()+1][sequence2.length()+1][4];
	}
	
	public int[][][] getScoreTable(){
		ensureTableIsFilledIn();
		
		int[][][] matrix= new int[scoreTable.length][scoreTable[0].length][scoreTable[0][0].length];
		
		for(int i=0;i<scoreTable.length;++i){
			for(int j=0;j<scoreTable[i].length;++j){
				for(int h=0;h<scoreTable[i][j].length;++h){
					matrix[i][j][h] = scoreTable[i][j][h].getScore();
				}
			}
		}
		
		return matrix;
	}
	
	protected void initialize() {
		for(int i=0;i<scoreTable.length;++i){
			for(int j=0;j<scoreTable[i].length;++j){
				for(int h=0;h<scoreTable[i][j].length;++h){
					scoreTable[i][j][h] = new Cell3d(i,j,h);
				}
			}
		}
		initializeScores();
		initializePointers();
		initializeKmers();
	
		isInitialized = true;	
	}
	
	protected void initializeScores(){
		for(int i=0;i<scoreTable.length;++i){
			for(int j=0;j<scoreTable[i].length;++j){
				for(int h=0;h<scoreTable[i][j].length;++h){
					scoreTable[i][j][h].setScore(getInitialScore(i,j,h));
				}
			}
		}
	}
	
	protected void initializePointers(){
		for(int i=0;i<scoreTable.length;++i){
			for(int j=0;j<scoreTable[i].length;++j){
				for(int h=0;h<scoreTable[i][j].length;++h){
					scoreTable[i][j][h].setPrevCell(getInitialPointer(i,j,h));
				}
			}
		}
	}
	
	protected void initializeKmers(){
		for(int i=0;i<scoreTable.length;++i){
			for(int j=0;j<scoreTable[i].length;++j){
				for(int h=0;h<scoreTable[i][j].length;++h){
					scoreTable[i][j][h].setKmer(getInitialKmer(i,j,h));
				}
			}
		}
	}
	
	protected abstract String getInitialKmer(int i, int j, int h);
	
	protected abstract int getInitialScore(int row, int col, int height);
	
	protected abstract Cell getInitialPointer(int row, int col, int height);
	
	protected abstract void fillInCell(Cell currentCell,Cell cellAbove, Cell cellToLeft, Cell cellAboveLeft);
	
	protected void fillIn(){
		for(int i=0;i<scoreTable.length;++i){
			for(int j=0;j<scoreTable[i].length;++j){
				for(int h=0;h<scoreTable[i][j].length;++h){
					Cell currentCell= scoreTable[i][j][h];
					Cell cellAbove= scoreTable[i-1][j][h];
					Cell cellToLeft= scoreTable[i][j-1][h];
					Cell cellAboveLeft= scoreTable[i-1][j-1][h];
					fillInCell(currentCell, cellAbove, cellToLeft, cellAboveLeft);
				}
			}
		}
		
		tableIsFilledIn=true;
	}
	
	abstract protected Object getTraceback();
	
	protected void ensureTableIsFilledIn(){
		if(!isInitialized){
			initialize();
		}
		if(!tableIsFilledIn){
			fillIn();
		}
	}
}
